# 版本11.0：2024/12/9  Hanpeng Lin
'''
Final Edition
Author:Hanpeng Lin, Zhekai Zhang, Xiao Tang
Time: 2024/12/9 
Email: linhp7@mail2.sysu.edu.cn
Introduction:
整合加入了自实现函数作为第5、6、7方法以供该选择
分别对应nelder_mead、powell_algorithm、steepest_descent
'''
# 导入系统模块，用于与操作系统交互
import sys
# 导入NumPy库，用于数组和数学运算
import numpy as np
# 导入Matplotlib库，用于绘制图表
import matplotlib.pyplot as plt
from matplotlib import rcParams
# 导入PyQt5库，用于图形界面
from PyQt5 import QtWidgets, QtCore
# 导入定时器类，用于定时触发事件
from PyQt5.QtCore import QTimer
# 用于Matplotlib与PyQt集成
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas, NavigationToolbar2QT
# 导入SciPy优化库中的最小化函数
from scipy.optimize import minimize, minimize_scalar
# 设置中文显示字体
rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体字体
rcParams['axes.unicode_minus'] = False  # 显示负号
# 引用自实现函数
from steepest_descent import steepest_descent
from powell_algorithm import *
from nelder_mead import *

global choice
choice = 1


# 测试函数1
def height_function(x, y):
    if choice == 1:
        return 10 * np.sin(0.1 * x) * np.cos(0.1 * y)
    elif choice == 2:
        return (1 - x)**2 + 100 * (y - x**2)**2
    elif choice == 3:
        return (1.5 - x + x*y)**2+(2.25 - x + x * y**2)**2 + (2.625 -x + x * y**3)**2

def gradient(x, y):
    if choice == 1:
        grad_x = 1 * np.cos(0.1 * x) * np.cos(0.1 * y)
        grad_y = -1 * np.sin(0.1 * x) * np.sin(0.1 * y)
    elif choice == 2:
        grad_x = 2 * (x - 1) - 400 * (y - x ** 2) * x
        grad_y = 200*(y-x**2)
    elif choice == 3:
        grad_x = 2 * (1.5 - x + x * y) * (-1 + y) + 2 * (2.25 - x + x * y ** 2) * (-1 + y ** 2) + 2 * (2.625 - x + x * y ** 3) * (-1 + y ** 3)
        grad_y = 2*(1.5 - x + x*y)*x + 2*(2.25 - x + x * y**2)*2*x*y + 2*(2.625 -x + x * y**3)*3*x*y**2
    return np.array([grad_x, grad_y])

def func(x):
    if choice == 1:
        return 10 * np.sin(0.1 * x[0]) * np.cos(0.1 * x[1])
    elif choice == 2:
        return (1 - x[0]) ** 2 + 100 * (x[1] - x[0] ** 2) ** 2
    elif choice == 3:
        return (1.5 - x[0] + x[0] * x[1]) ** 2 + (2.25 - x[0] + x[0] * x[1] ** 2) ** 2 + (
                    2.625 - x[0] + x[0] * x[1] ** 3) ** 2

# # 测试函数2
# def height_function2(x, y):
#     return (1 - x)**2 + 100 * (y - x**2)**2
#
# def gradient2(x, y):
#     grad_x = 2*(x-1) - 400*(y-x**2)*x
#     grad_y = 200*(y-x**2)
#     return np.array([grad_x, grad_y])
#
# def func2(x):
#     return (1 - x[0])**2 + 100 * (x[1] - x[0]**2)**2
#
# # 测试函数3
# def height_function3(x, y):
#     return (1.5 - x + x*y)**2+(2.25 - x + x * y**2)**2 + (2.625 -x + x * y**3)**2
#
# def gradient3(x, y):
#     grad_x = 2*(1.5 - x + x*y)*(-1+y) + 2*(2.25 - x + x * y**2)*(-1+y**2) + 2*(2.625 -x + x * y**3)*(-1+y**3)
#     grad_y = 2*(1.5 - x + x*y)*x + 2*(2.25 - x + x * y**2)*2*x*y + 2*(2.625 -x + x * y**3)*3*x*y**2
#     return np.array([grad_x, grad_y])
#
# def func3(x):
#     return (1.5 - x[0] + x[0]*x[1])**2+(2.25 - x[0] + x[0] * x[1]**2)**2 + (2.625 -x[0] + x[0] * x[1]**3)**2


# 梯度下降法
def gradient_descent(x, y, learning_rate=100):
    grad_x, grad_y = gradient(x, y)         # 获取当前点的梯度
    new_x = x - learning_rate * grad_x      # 更新x坐标
    new_y = y - learning_rate * grad_y      # 更新y坐标
    return new_x, new_y                     # 返回更新后的坐标


# Nelder-Mead 算法包装
def nelder_mead_wrapper(xy):
    return height_function(xy[0], xy[1])


# Powell 算法包装
def powell_wrapper(xy):
    return height_function(xy[0], xy[1])


# 最速下降法包装
def steepest_descent_line_search(x, y, alpha):
    grad = gradient(x, y)                   # 获取当前点的梯度
    new_x = x - alpha * grad[0]             # 更新x坐标
    new_y = y - alpha * grad[1]             # 更新y坐标
    return height_function(new_x, new_y)    # 返回更新后点的高度函数值


class App(QtWidgets.QMainWindow):           # 定义主应用程序类，继承自QtWidgets.QMainWindow
    def __init__(self):
        super().__init__()                  # 调用父类构造函数
        self.initUI()                       # 初始化界面
        self.learning_rate = 0.1           # 设置学习率
        self.x, self.y = 0.0, 0.0           # 初始化x, y坐标
        self.iteration = 0                  # 迭代计数器
        self.is_paused = True               # 是否暂停标志
        self.path = []                      # 记录路径
        self.points_2d = []                 # 保存二维图中所有点的列表
        self.initial_points = []            # 存储初始点
        self.simplex = None                 # 初始化simplex（Nelder-Mead算法的初始点集合）
        self.result = None                  # 最优化结果
        self.first_run = True               # Nelder-Mead（自实现）第一次运行标识
        self.continuousRunTimer = QTimer(self)                      # 创建定时器
        self.continuousRunTimer.timeout.connect(self.runAlgorithm)  # 定时器超时连接到算法运行函数
        # 初始化收敛曲线数据
        self.convergence_values = []        # 用于存储每次迭代的目标函数值

    def initUI(self):
        self.setWindowTitle('优化算法可视化')              # 设置窗口标题
        self.setGeometry(400, 150, 1100, 1200)     # 设置窗口位置和大小
        central_widget = QtWidgets.QWidget(self)        # 创建中央控件
        self.setCentralWidget(central_widget)           # 设置中央控件
        layout = QtWidgets.QVBoxLayout(central_widget)  # 创建垂直布局
        hlayout1 = QtWidgets.QHBoxLayout()              # 创建水平布局
        hlayout2 = QtWidgets.QHBoxLayout()              # 创建水平布局
        hlayout3 = QtWidgets.QHBoxLayout()              # 创建水平布局

        # 算法选择按钮
        button_size1 = QtCore.QSize(1080, 45)                                 # 按钮大小
        self.algorithmComboBox = QtWidgets.QComboBox()                              # 创建下拉框
        self.algorithmComboBox.addItem("方法1: 梯度下降法")  # 添加下拉项
        self.algorithmComboBox.addItem("方法2: Nelder-Mead算法(scipy)")
        self.algorithmComboBox.addItem("方法3: Powell算法(scipy)")
        self.algorithmComboBox.addItem("方法4: 最速下降法")
        self.algorithmComboBox.addItem("方法5: Nelder-Mead算法(自实现)")
        self.algorithmComboBox.addItem("方法6: Powell算法(自实现)")
        self.algorithmComboBox.addItem("方法7: 最速下降法(自实现)")
        self.algorithmComboBox.currentIndexChanged.connect(self.algorithmSelected)  # 选择改变时连接到算法选择函数
        self.algorithmComboBox.setFixedSize(button_size1)                           # 设置下拉框大小
        layout.addWidget(self.algorithmComboBox)                                    # 添加到布局

        # 函数选择按钮
        self.algorithmComboBox1 = QtWidgets.QComboBox()  # 创建下拉框
        self.algorithmComboBox1.addItem("测试函数1: 10 * np.sin(0.1 * x[0]) * np.cos(0.1 * x[1])")
        self.algorithmComboBox1.addItem("测试函数2: Rosenbrock函数")
        self.algorithmComboBox1.addItem("测试函数3: Beale函数")
        self.algorithmComboBox1.currentIndexChanged.connect(self.algorithmSelected1)  # 选择改变时连接到算法选择函数
        self.algorithmComboBox1.setFixedSize(button_size1)  # 设置下拉框大小
        layout.addWidget(self.algorithmComboBox1)  # 添加到布局

        button_size2 = QtCore.QSize(125, 40)     # 设置按钮大小
        button_size3 = QtCore.QSize(400, 40)     # 设置按钮大小
        self.labelX = QtWidgets.QLabel('  初始x坐标:')    # 标签：初始x坐标
        self.labelY = QtWidgets.QLabel('  初始y坐标:')    # 标签：初始y坐标
        self.label1 = QtWidgets.QLabel('  反射系数')    # 标签：初始
        self.label2 = QtWidgets.QLabel('  扩展系数:')    # 标签：初始
        self.label3 = QtWidgets.QLabel('  收缩系数:')    # 标签：初始
        self.label4 = QtWidgets.QLabel('  压缩系数:')    # 标签：初始
        self.lineEditX = QtWidgets.QLineEdit('0')       # 创建输入框，默认值为0
        self.lineEditY = QtWidgets.QLineEdit('0')       # 创建输入框，默认值为0
        self.lineEdit1 = QtWidgets.QLineEdit('0.5')       # 创建输入框，默认值为0
        self.lineEdit2 = QtWidgets.QLineEdit('0.5')       # 创建输入框，默认值为0
        self.lineEdit3 = QtWidgets.QLineEdit('0.5')       # 创建输入框，默认值为0
        self.lineEdit4 = QtWidgets.QLineEdit('0.5')       # 创建输入框，默认值为0
        self.labelX.setFixedSize(button_size2)          # 设置标签大小
        self.labelY.setFixedSize(button_size2)          # 设置标签大小
        self.label1.setFixedSize(button_size2)          # 设置标签大小
        self.label2.setFixedSize(button_size2)          # 设置标签大小
        self.label3.setFixedSize(button_size2)          # 设置标签大小
        self.label4.setFixedSize(button_size2)          # 设置标签大小
        self.lineEditX.setFixedSize(button_size3)       # 设置输入框大小
        self.lineEditY.setFixedSize(button_size3)       # 设置输入框大小
        self.lineEdit1.setFixedSize(button_size3)       # 设置输入框大小
        self.lineEdit2.setFixedSize(button_size3)       # 设置输入框大小
        self.lineEdit3.setFixedSize(button_size3)       # 设置输入框大小
        self.lineEdit4.setFixedSize(button_size3)       # 设置输入框大小
        hlayout1.addWidget(self.labelX)                 # 将标签添加到水平布局
        hlayout1.addWidget(self.lineEditX)              # 将输入框添加到水平布局
        hlayout1.addWidget(self.labelY)                 # 将标签添加到水平布局
        hlayout1.addWidget(self.lineEditY)              # 将输入框添加到水平布局
        hlayout2.addWidget(self.label1)                 # 将标签添加到水平布局
        hlayout2.addWidget(self.lineEdit1)              # 将输入框添加到水平布局
        hlayout2.addWidget(self.label2)                 # 将标签添加到水平布局
        hlayout2.addWidget(self.lineEdit2)              # 将输入框添加到水平布局
        hlayout3.addWidget(self.label3)                 # 将标签添加到水平布局
        hlayout3.addWidget(self.lineEdit3)              # 将输入框添加到水平布局
        hlayout3.addWidget(self.label4)                 # 将标签添加到水平布局
        hlayout3.addWidget(self.lineEdit4)              # 将输入框添加到水平布局
        layout.addLayout(hlayout1)                      # 将水平布局添加到垂直布局
        layout.addLayout(hlayout2)                      # 将水平布局添加到垂直布局
        layout.addLayout(hlayout3)                      # 将水平布局添加到垂直布局

        self.label1.setVisible(False)
        self.lineEdit1.setVisible(False)
        self.label2.setVisible(False)
        self.lineEdit2.setVisible(False)
        self.label3.setVisible(False)
        self.lineEdit3.setVisible(False)
        self.label4.setVisible(False)
        self.lineEdit4.setVisible(False)

        # 按钮
        button_layout = QtWidgets.QGridLayout()
        button_size = QtCore.QSize(260, 50)
        self.runButton = QtWidgets.QPushButton('迭代一次')
        self.runButton.setFixedSize(button_size)
        self.runButton.clicked.connect(self.runAlgorithm)
        button_layout.addWidget(self.runButton, 0, 0)
        self.startContinuousButton = QtWidgets.QPushButton('自动迭代')
        self.startContinuousButton.setFixedSize(button_size)
        self.startContinuousButton.clicked.connect(self.startContinuousRun)
        button_layout.addWidget(self.startContinuousButton, 0, 1)
        self.stopContinuousButton = QtWidgets.QPushButton('停止迭代')
        self.stopContinuousButton.setFixedSize(button_size)
        self.stopContinuousButton.clicked.connect(self.stopContinuousRun)
        button_layout.addWidget(self.stopContinuousButton, 0, 2)
        self.resetButton = QtWidgets.QPushButton('复位')
        self.resetButton.setFixedSize(button_size)
        self.resetButton.clicked.connect(self.reset)
        button_layout.addWidget(self.resetButton, 0, 3)
        layout.addLayout(button_layout)

        # # 添加三维画布
        # self.fig = plt.figure()
        # self.ax = self.fig.add_subplot(111, projection='3d')
        # self.canvas = FigureCanvas(self.fig)
        # layout.addWidget(self.canvas)
        # layout.setStretchFactor(self.canvas, 100)

        # # 添加导航工具栏
        # self.mpl_toolbar = NavigationToolbar2QT(self.canvas, self)
        # layout.addWidget(self.mpl_toolbar)

        # 创建收敛曲线的绘图区域
        self.fig2 = plt.figure(figsize=(10, 1))  # 创建一个新的图形，用于收敛曲线
        self.ax2 = self.fig2.add_subplot(111)  # 新的子图
        self.canvas2 = FigureCanvas(self.fig2)  # 收敛曲线的canvas

        # 添加二维画布
        self.fig3 = plt.figure(figsize=(5, 5))
        self.ax3 = self.fig3.add_subplot(111)
        self.canvas3 = FigureCanvas(self.fig3)

        # 布局部分，增加一个新的区域放置收敛曲线
        hlayout = QtWidgets.QHBoxLayout()
        # hlayout.addWidget(self.canvas)  # 3D图canvas
        hlayout.addWidget(self.canvas3)  # 2D等高线图canvas
        layout.addLayout(hlayout)  # 将这个布局加入主布局

        hlayout2 = QtWidgets.QHBoxLayout()  # 创建一个新的水平布局
        hlayout2.addWidget(self.canvas2)  # 将canvas2（收敛曲线）加入布局
        layout.addLayout(hlayout2)  # 将这个布局加入主布局

        # 绘制三维地形图
        self.draw_terrain()

    def draw_terrain(self):
        print(choice)
        # # 绘制三维地形图
        # self.ax.clear()
        X = np.linspace(-30, 30, 2000)
        Y = np.linspace(-30, 30, 2000)
        X, Y = np.meshgrid(X, Y)
        Z = height_function(X, Y)

        # self.ax.plot_surface(X, Y, Z, cmap='terrain', alpha=0.7)

        # # 绘制三维图的等高线
        # self.contour = self.ax.contour(X, Y, Z, 10, cmap='jet', offset=np.min(Z) - 1, alpha=0.5)
        # self.ax.set_xlabel('X1 axis')
        # self.ax.set_ylabel('X2 axis')
        # self.ax.set_zlabel('Func(X1,X2) axis')

        # 绘制二维等高线图
        self.ax3.clear()  # 清除之前的图形
        self.ax3.contour(X, Y, Z, 30, cmap='jet')  # 绘制二维等高线图
        self.ax3.set_xlabel('X1 axis')
        self.ax3.set_ylabel('X2 axis')
        self.ax3.set_title('等高线图')

        # self.canvas.draw()  # 更新三维图
        self.canvas3.draw()  # 更新二维图

    def update_convergence_plot(self):
        self.ax2.clear()        # 清空之前的收敛曲线图
        self.ax2.plot(self.convergence_values, label="收敛曲线")  # 绘制收敛曲线
        self.ax2.set_xlabel("迭代次数")
        self.ax2.set_ylabel("目标函数值")
        self.ax2.set_title("优化过程的收敛曲线")
        self.ax2.legend()       # 显示图例
        self.ax2.grid(True)     # 添加网格线

        # 检查self.convergence_values是否为空
        if len(self.convergence_values) == 0:
            current_value = 0  # 如果为空，设定一个默认值
            best_value = 0  # 历史最优值也设定为0
        else:
            # 当前函数值
            current_value = self.convergence_values[-1]  # 假设当前函数值是最后一个收敛值
            # 历史最优函数值
            best_value = min(self.convergence_values)  # 假设历史最优是收敛值的最小值

        # 清除所有现有的文本
        if hasattr(self, 'info_texts'):
            for text in self.info_texts:
                text.set_visible(False)

        # 固定位置显示当前函数值和历史最优函数值
        self.ax2.text(0.95, 0.9, f"当前函数值: {current_value:.4f}",
                      ha='right', va='top', transform=self.ax2.transAxes, fontsize=10, color='r')
        self.ax2.text(0.95, 0.85, f"历史最优值: {best_value:.4f}",
                      ha='right', va='top', transform=self.ax2.transAxes, fontsize=10, color='g')

        self.canvas2.draw()  # 更新收敛曲线图

    def startContinuousRun(self):
        # 启动自动迭代
        self.is_paused = False  # 取消暂停
        self.continuousRunTimer.start(10)  # 设置定时器触发时间为50毫秒

    def stopContinuousRun(self):
        # 停止自动迭代
        self.is_paused = True  # 设置为暂停状态
        self.continuousRunTimer.stop()  # 停止定时器

    def runAlgorithm(self):
        if self.algorithmComboBox.currentIndex() == 0:
            self.runGradientDescent()
        elif self.algorithmComboBox.currentIndex() == 1:
            self.runNelderMead()
        elif self.algorithmComboBox.currentIndex() == 2:
            self.runPowell()
        elif self.algorithmComboBox.currentIndex() == 3:
            self.runSteepestDescent()
        elif self.algorithmComboBox.currentIndex() == 4:
            self.runfuncNelderMead()
        elif self.algorithmComboBox.currentIndex() == 5:
            self.runfuncPowellAlporithm()
        elif self.algorithmComboBox.currentIndex() == 6:
            self.runfuncSteepestDescent()

    def runGradientDescent(self):
        current_x = float(self.lineEditX.text())
        current_y = float(self.lineEditY.text())

        # 执行梯度下降优化
        self.x, self.y = gradient_descent(current_x, current_y, self.learning_rate)

        # 更新UI显示新的x和y值
        self.lineEditX.setText(str(self.x))
        self.lineEditY.setText(str(self.y))

        # # 绘制当前x, y位置的目标函数值
        # self.ax.plot([self.x], [self.y], [height_function(self.x, self.y)], 'bo')

        # # 在三维图中绘制当前x, y位置的目标函数值
        # self.ax.scatter(self.x, self.y, height_function(self.x, self.y), color='blue')  # 使用scatter标记点
        # self.ax.text(self.x, self.y, height_function(self.x, self.y), f'({self.x:.2f}, {self.y:.2f})',
        #              color='black')  # 添加点的注释

        # 绘制二维图中的点
        self.points_2d.append((self.x, self.y))  # 保存当前二维坐标点
        self.ax3.scatter(self.x, self.y, color='red')  # 在二维图中绘制点

        # 记录目标函数值，更新收敛曲线
        self.convergence_values.append(height_function(self.x, self.y))  # 保存当前的目标函数值

        # 更新收敛曲线图
        self.update_convergence_plot()  # 调用方法更新收敛曲线图

        # 刷新2D图3D图和收敛图
        # self.canvas.draw()
        # self.canvas.flush_events()
        self.canvas3.draw()

    def runNelderMead(self):
        if self.iteration == 0:
            self.initial_points = [np.random.uniform(-15, 15, 2) for _ in range(5)]
            self.simplex = np.array(self.initial_points)
            self.result = minimize(nelder_mead_wrapper, self.simplex[0], method='Nelder-Mead', options={'maxiter': 1})
        else:
            self.result = minimize(nelder_mead_wrapper, self.result.x, method='Nelder-Mead', options={'maxiter': 1})

        self.simplex = self.result.x.reshape(-1, 2)
        self.iteration += 1

        # self.ax.plot(self.simplex[:, 0], self.simplex[:, 1], [height_function(p[0], p[1]) for p in self.simplex], 'ro')
        # self.canvas.draw()
        # self.canvas.flush_events()

        # 绘制二维图中的点
        self.points_2d.append((self.result.x[0], self.result.x[1]))  # 保存当前二维坐标点
        self.ax3.scatter(self.result.x[0], self.result.x[1], color='red')  # 在二维图中绘制点

        self.canvas3.draw()

        # 记录目标函数值，更新收敛曲线
        self.convergence_values.append(self.result.fun)  # 保存当前的目标函数值
        self.update_convergence_plot()  # 更新收敛曲线

    def runPowell(self):
        if self.iteration == 0:
            self.initial_points = np.random.uniform(-15, 15, 2)
            self.result = minimize(powell_wrapper, self.initial_points, method='Powell',
                                   options={'maxiter': 1, 'disp': False})
            self.path = [self.initial_points]
        else:
            self.result = minimize(powell_wrapper, self.result.x, method='Powell',
                                   options={'maxiter': 1, 'disp': False})

        self.path.append(self.result.x)
        self.iteration += 1

        # self.ax.clear()
        # self.draw_terrain()
        # path = np.array(self.path)
        # self.ax.plot(path[:, 0], path[:, 1], [height_function(p[0], p[1]) for p in path], 'ro-')

        # 绘制二维图中的点
        self.points_2d.append((self.result.x[0], self.result.x[1]))  # 保存当前二维坐标点
        self.ax3.scatter(self.result.x[0], self.result.x[1], color='red')  # 在二维图中绘制点

        # self.canvas.draw()
        self.canvas3.draw()
        # self.canvas.flush_events()

        # 记录目标函数值，更新收敛曲线
        self.convergence_values.append(self.result.fun)  # 保存当前的目标函数值
        self.update_convergence_plot()  # 更新收敛曲线

    def runSteepestDescent(self):
        current_x = float(self.lineEditX.text())
        current_y = float(self.lineEditY.text())

        def line_search_func(alpha):
            return steepest_descent_line_search(current_x, current_y, alpha)

        res = minimize_scalar(line_search_func, bounds=(0, 1), method='bounded')
        alpha_opt = res.x

        grad = gradient(current_x, current_y)
        self.x = current_x - alpha_opt * grad[0]
        self.y = current_y - alpha_opt * grad[1]

        self.lineEditX.setText(str(self.x))
        self.lineEditY.setText(str(self.y))

        self.path.append((self.x, self.y))
        # self.ax.clear()
        # self.draw_terrain()
        # path = np.array(self.path)
        # self.ax.plot(path[:, 0], path[:, 1], [height_function(p[0], p[1]) for p in path], 'bo-')
        # self.canvas.draw()
        # self.canvas.flush_events()

        # 绘制二维图中的点
        self.points_2d.append((self.x, self.y))  # 保存当前二维坐标点
        # print(self.points_2d)
        # self.ax3.scatter(self.x, self.y, color='red')  # 在二维图中绘制点

        # 更新二维图
        for point in self.points_2d:  # 绘制所有路径点
            self.ax3.scatter(point[0], point[1], color='red')

        self.canvas3.draw()

        # 记录目标函数值，更新收敛曲线
        self.convergence_values.append(height_function(self.x, self.y))  # 保存当前的目标函数值
        self.update_convergence_plot()  # 更新收敛曲线

    def runfuncNelderMead(self):
        current_x = float(self.lineEditX.text())
        current_y = float(self.lineEditY.text())
        # r: 反射系数 (默认: 1)
        # e: 扩展系数 (默认: 0.5)
        # c: 收缩系数 (默认: 0.5)
        # s: 压缩系数 (默认: 0.5)
        r = float(self.lineEdit1.text())
        e = float(self.lineEdit2.text())
        c = float(self.lineEdit3.text())
        s = float(self.lineEdit4.text())

        if self.first_run:
            # 初始偏移量
            delta = 20

            # 生成初始的三个点
            X = np.array([
                [current_x, current_y],
                [current_x + delta, current_y],
                [current_x, current_y - delta]
            ])

            # 标记初始化完成
            self.first_run = False
        else:
            # 使用上一次的simplex作为新的初始点
            X = self.simplex

        res, history_f = nelder_mead(func, X, r, e, c, s)
        print(res)

        self.lineEditX.setText(str(res[0][0][0]))
        self.lineEditY.setText(str(res[0][0][1]))

        self.path.append((res[0][0][0], res[0][0][1]))
        # self.ax.clear()
        # self.draw_terrain()
        # path = np.array(self.path)
        # self.ax.plot(path[:, 0], path[:, 1], [height_function(p[0], p[1]) for p in path], 'bo-')
        # self.canvas.draw()
        # self.canvas.flush_events()

        # 绘制二维图中的点
        self.points_2d.append((res[0][0][0], res[0][0][1]))  # 保存当前二维坐标点

        # 更新二维图
        for point in self.points_2d:  # 绘制所有路径点
            self.ax3.scatter(point[0], point[1], color='red')

        self.canvas3.draw()

        # 记录目标函数值，更新收敛曲线
        self.convergence_values.append(height_function(res[0][0][0], res[0][0][1]))  # 保存当前的目标函数值
        self.update_convergence_plot()  # 更新收敛曲线
        # 更新simplex为最新的结果
        self.simplex = res[0]


    def runfuncSteepestDescent(self):
        current_x = float(self.lineEditX.text())
        current_y = float(self.lineEditY.text())

        if choice == 1:
            res = steepest_descent("10 * sin(0.1 * x) * cos(0.1 * y)", [current_x, current_y])
        elif choice == 2:
            res = steepest_descent("(1 - x)**2 + 100 * (y - x**2)**2", [current_x, current_y])
        elif choice == 3:
            res = steepest_descent("(1.5 - x + x*y)**2+(2.25 - x + x * y**2)**2 + (2.625 -x + x * y**3)**2", [current_x, current_y])

        self.lineEditX.setText(str(res[0][0]))
        self.lineEditY.setText(str(res[0][1]))

        self.path.append((res[0][0], res[0][1]))
        # self.ax.clear()
        # self.draw_terrain()
        # path = np.array(self.path)
        # self.ax.plot(path[:, 0], path[:, 1], [height_function(p[0], p[1]) for p in path], 'bo-')
        # self.canvas.draw()
        # self.canvas.flush_events()

        # 绘制二维图中的点
        self.points_2d.append((res[0][0], res[0][1]))  # 保存当前二维坐标点

        # 更新二维图
        for point in self.points_2d:  # 绘制所有路径点
            self.ax3.scatter(point[0], point[1], color='red')

        self.canvas3.draw()

        # 记录目标函数值，更新收敛曲线
        self.convergence_values.append(height_function(res[0][0], res[0][1]))  # 保存当前的目标函数值
        self.update_convergence_plot()  # 更新收敛曲线

    def runfuncPowellAlporithm(self):
        current_x = float(self.lineEditX.text())
        current_y = float(self.lineEditY.text())

        res = powell_algorithm(func, [current_x, current_y])

        self.lineEditX.setText(str(res[0]))
        self.lineEditY.setText(str(res[1]))

        self.path.append((res[0], res[1]))
        # self.ax.clear()
        # self.draw_terrain()
        # path = np.array(self.path)
        # self.ax.plot(path[:, 0], path[:, 1], [height_function(p[0], p[1]) for p in path], 'bo-')
        # self.canvas.draw()
        # self.canvas.flush_events()

        # 绘制二维图中的点
        self.points_2d.append((res[0], res[1]))  # 保存当前二维坐标点

        # 更新二维图
        for point in self.points_2d:  # 绘制所有路径点
            self.ax3.scatter(point[0], point[1], color='red')

        self.canvas3.draw()

        # 记录目标函数值，更新收敛曲线
        self.convergence_values.append(height_function(res[0], res[1]))  # 保存当前的目标函数值
        self.update_convergence_plot()  # 更新收敛曲线

    def reset(self):
        self.is_paused = True
        self.continuousRunTimer.stop()
        self.x, self.y = 0.0, 0.0
        self.iteration = 0
        self.path.clear()
        self.initial_points = None
        self.simplex = None
        self.result = None
        self.convergence_values.clear()  # 清空收敛曲线数据
        self.lineEditX.setText('0')
        self.lineEditY.setText('0')
        self.points_2d.clear()
        # self.ax.clear()
        self.ax3.clear()
        self.draw_terrain()
        self.update_convergence_plot()  # 清空并更新收敛曲线图
        self.first_run = True  # 重置first_run标志为True

    def algorithmSelected(self, index):
        self.labelX.setVisible(True)
        self.lineEditX.setVisible(True)
        self.labelY.setVisible(True)
        self.lineEditY.setVisible(True)

        
        if index == 4:
            self.label1.setVisible(True)
            self.lineEdit1.setVisible(True)
            self.label2.setVisible(True)
            self.lineEdit2.setVisible(True)
            self.label3.setVisible(True)
            self.lineEdit3.setVisible(True)
            self.label4.setVisible(True)
            self.lineEdit4.setVisible(True)
        else:
            self.label1.setVisible(False)
            self.lineEdit1.setVisible(False)
            self.label2.setVisible(False)
            self.lineEdit2.setVisible(False)
            self.label3.setVisible(False)
            self.lineEdit3.setVisible(False)
            self.label4.setVisible(False)
            self.lineEdit4.setVisible(False)

    def algorithmSelected1(self, index):
        global choice
        if index == 0:
            choice = 1
        elif index == 1:
            choice = 2
        elif index == 2:
            choice = 3


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    ex = App()
    ex.show()
    sys.exit(app.exec_())
